D@@customer.scx, frmcustomer, error tsgen.vcx, customerinfo, errortastrade.dbc, valorder, xtsbase.vcx, tsbaseform, first tsbase.vcx, tsbaseform, prior tsbase.vcx, tsbaseform, next tsbase.vcx, tsbaseform, last!customer.scx, frmcustomers, error tsbase.vcx, tspasswordtextbox, *tOrder numbers are generated in the same way that unique IDs are generated for records. The default value expression for the order_number field in the orders table is NewID("order_number"). Passing a parameter to the NewID( ) stored procedure causes it to use the value of the parameter instead of the value of the ALIAS( ) function to look up the ID in the Setup table. U]?7Itsbase.vcx, tsbaseform, addtomenu tsbase.vcx, tsbaseform, removefrommenuabout.vcx, aboutbox, init:behindsc.scx, cmdcode, click behindsc.scx, frmbehindsc, *The Find button (cmdFind) allows a user to choose a particular order to display in the Order Entry form. The following line of code in cmdFind.Click creates an instance of the FindOrder form (in tsgen.vcx): lcOrder_ID = oApp.DoFormRetVal("FindOrder") The FindOrder form displays a list of all orders and allows a user to sort by order_id or cust_id. For example, the following code is associated with the Click event of the Order ID option button: SET ORDER TO order_id THISFORM.lstCustomers.Requery Achngpswd.scx, cmdbehindsc, click behindsc.scx, frmbehindsc, initGchngpswd.scx, cmdok, click chngpswd.scx, frmchangepassword, validate Hiding login passwords was accomplished by setting the PasswordChar property of the password text box to the "*" (asterisk) character. ed. Code associated with this event creates atastrade.dbc, newid,ordentry.scx, cmdFind, click, valid/chngpswd.scx, txtoldpassword, interactivechangeIordentry.scx, cmdavailablecredit, click tastrade.dbc, remainingcredit, Since the OK button has its Default property set to .T., the code in the button's Click event will execute anytime the user hits the Enter key in this form. Code in the OK button's Click event calls the custom Validate( ) method of the form. The custom Validate() method contains code to ensure that: 1. The original password was entered. 2. A new password was entered. 3. The value of the Confirm New Password textbox equals the value of the New Password text box. HjTMost forms in Tasmanian Traders are run through the custom DoForm( ) method of the Application class. Currently, code in this method just runs the form with the DO FORM command. However, by running all forms through a common method such as this, it's easy to make future modifications to the way all forms are run. For example, the DoForm method would be a good place to check if enough memory is available before running a form. rkDd.tsbase.vcx, tsbaseform, saveKtsgen.vcx, daterange, * tsgen.vcx, txtdatefrom, * tsgen.vcx, txtdateto, *To close the application and return to Visual FoxPro, the user selects the Return to Visual FoxPro option from the File menu. The menu code calls the global application object's custom CleanUp( ) method to clean up the environment, and then releases the global application object. 7}b`? E;4MU4CJƱDmordhist.scx, frmordhistory, init tsgen.vcx, application, addinstance tsgen.vcx, application, removeinstance(tsbase.vcx, tsgrid, (refresh, sumcolumn)Creports.scx, opgoutputtype, click reports.scx, frmreports, refreshmThe custom Restore( ) method is called whenever the user chooses Restore from the File menu or clicks the Restore button on the toolbar. Since row and table buffering are used throughout Tastrade, the TABLEREVERT( ) function is called to update the bound controls on the form with the values in the current record. The form is then refreshed to display the values. KGID'ordhist.scx, frmordhistory, refreshformreports.scx, cmdrun, clickcategory.scx, cmdpicture, *7login.vcx, cboname, * login.vcx, loginpicture, refreshThe custom Save( ) method first calls the custom WriteBuffer( ) method to ensure that the values of the current control are written to the buffer. Next, it calls the TABLEUPDATE( ) function to update the changes. ˉEGtTo ensure that Visual FoxPro re-evaluates the SKIP FOR clauses in menus, the following command is included in the code associated with the Activate event of the forms: ACTIVATE MENU _MSYSMENU NOWAIT Eۄ%Ӻl_pՀO4-> b2KB[ utility.prg,,tsgen.vcx, application, cleanupordhist.scx, cmdconfirm, click main.vcx, tastrade, getuserleveltsgen.vcx, application, doform6tsgen.vcx, introform, * tsbase.vcx, tsformretval, * |+y3\Й?~Φ]siwItastrade.dbc, remainingcredit, , ordentry.scx, cmdavailablecredit, click tsbase.vcx, tsbaseform, activate#ordentry.scx, cmdlastorder, click Lordentry.scx, grdlineitems, rightclick ordentry.scx, frmorderentry, gridpopThere are two field rules for the Customer table. 1. The customer ID cannot be empty, null, or blank. 2. The minimum order amount cannot exceed the maximum order amount. These rules are defined as expressions directly in the Table Designer. The Validation property of the customer_id field is: NOT EMPTY(customer_id) The Validation property of the min_order_amt field is: min_order_amt <= max_order_amt If a rule is violated, the form's Error( ) event is triggered, and for both the Customer form and the Add Customer form, the error is passed on to the CustomerInfo container object. This allows error handling to be defined once, regardless of how many forms are using the container.Z( qx(ٌ+۶YDRIk̩ Gn'AFtsgen.vcx, chkshowatstartup, * main.vcx, tastrade, init main.prg,, sIn the form's Load event code, the value of the employee's old password is stored to the cOldPassword property of the form. Code associated with the InteractiveChange event of the text box compares this value against the password the user is currently typing into the text box. When the values match, the Enabled property of the New and Confirm text boxes is set to .T..; tsbase.vcx, tsbaseform, activateBefore an order can be saved, it must meet a number of conditions: 1. There must be at least one line item. 2. The total of the current order plus the sum of all orders for the current customer must not exceed the customer's maximum order amount. 3. The total of the current order must be equal to or greater than the current customer's minimum order amount. ValOrder( ) performs all these checks and displays the appropriate error message if necessary. Note that the remaining conditions are not validated if a previous condition failed. If any condition fails, ValOrder( ) returns .F. ValOrder( ) is set as the table validation rule in the Properties dialog box for the Orders table.` 4fӷP$XEVe@ejGj{g+ycټ釩The Category form maintains all product categories. It was created from class tsMaintForm in TSBASE.VCX. Most of this form's functionality is defined in the tsMaintForm class, as well as in tsMaintForm's parent class, tsBaseForm. The picture of the category is displayed with an Image control. The Picture property of the Image control is set to a bitmap whose path is stored in the Products table. e embedded object from starting ifTasmanian Traders implements very basic security features based on each user's level. Each time the user logs in, the main menu, MAIN.MPR, is executed. Cleanup code for this menu conditionally releases menus based on the user level of the currently logged-in user. The Tastrade class in MAIN.VCX defines a custom method that returns the user level of the currently logged-in user: IF UPPER(oApp.GetUserLevel()) <> "APPLICATIONS DEVELOPER" RELEASE PAD Utilities OF _MSYSMENU ENDIF IF !INLIST(UPPER(oApp.GetUserLevel()), ; "APPLICATIONS DEVELOPER", "OPERATIONS MANAGER") RELEASE BAR 1 OF Administration && Login RELEASE BAR 2 OF Administration && Change Password RELEASE BAR 3 OF Administration && Separator ENDIF W,EY5eiWhenever a form is refreshed, the appropriate toolbar needs to be updated to ensure that it properly reflects the state of the current form. For example, if editing is not allowed for a form, then its lAllowEdits property will be .F. and the Save and Restore buttons on the toolbar should be disabled. The code associated with the form's Activate event first checks if the custom cToolBar property of the form is empty. If not, it then verifies that the toolbar object (which is a member of the global application object, oApp) exists. If it does, it calls the toolbar's Refresh( ) method to refresh the toolbar. nID of the currThere are two ways to access the on line help file from the Order Entry form. 1. Press F1. The HelpContextID property of the form is set to the context ID that was defined for the Order Entry form when the help file was created. See Part 2 of the Visual FoxPro Professional Features Guide for information on creating help files. When F1 is pressed, Visual FoxPro passes the HelpContextID property of the selected control to the internal help engine so that the appropriate topic is displayed. If the HelpContextID property of the selected control is 0, Visual FoxPro will attempt to use the control's parent's HelpContextID. Visual FoxPro will continue searching up the container hierarchy until it can go no further, at which point, the main contents of the help file are displayed. 2. Press the Help command button. Code in the Click( ) event of this button uses the ID clause of the HELP command so that the correct help window will be displayed. The Customers form maintains all customer information. This form differs from the rest of the maintenance forms in that the user is allowed to specify the primary key of the Customer table in the Customer ID text box. The parent class of the Customers form is tsMaintForm in TSBASE.VCX. Most of this form's functionality is defined in the tsMaintForm class or in tsMaintForm's parent class, tsBaseForm. U]?7U]?7U]?7U]?7U]?7U]?7U]?7U]?A filter is applied to the REPOLIST.DBF table to display either Reports (CTYPE = "REPO"), or Listings (CTYPE = "LIST"). The option button that the user selects determines which filter is applied. When the user selects either the Reports or Listings button, the form is refreshed, the first item in the list is selected, and focus is moved to the list box. Code in the form's Refresh( ) method is responsible for setting the filter and refreshing the list box. w crܚ_npLdMenu items are disabled in each menu's SKIP FOR clause. Menus that call methods on the active form are disabled if the active form is not an object. This is accomplished by a function call to the FormIsObject( ) function in UTILITY.PRG: FUNCTION FormIsObject() RETURN (TYPE("_SCREEN.ActiveForm") == "O" AND ; UPPER(_SCREEN.ActiveForm.BaseClass) = "FORM") ENDFUNC Menus that run forms that do not support multiple instances of themselves must be disabled if that form is already displayed. To check if the form is displayed, the WEXIST() function is passed the name of the form. For example, the following SKIP FOR clause would return .T. if the Products form already exists, thus disabling that particular menu: DEFINE BAR ...... SKIP FOR WEXIST("frmProducts")MYKʗ-<$5 SeUg.1|BLQQtƊSThe Order Entry form, which the Order History form is linked to, is stored as a custom property of the Order History form: oOrderEntryForm. Since the Order History form is running in its own private data session, local variables track both the DataSessionID of the current Order History form and the DataSessionID of the Order Entry form that the Order History form is linked to. Once these values are known, records in the "order history line items" view are scanned. For each record in that view where the exp_1 column is .T. (the column that the check box in the grid is bound to), values are stored to local variables (because the original fields can't be accessed once the data session is changed), the current data session is set to the DataSessionID of the Order Entry form, and the values are inserted into the order_line_items table. j;A4~]E`Allowing the user to enter primary key values can be problematic. In the past, the application had to include code that would prevent duplicate primary keys from being added to the table, but now Visual FoxPro enforces the uniqueness of values in primary key fields. Instead of allowing Visual FoxPro to inform the user in case a duplicate primary key is entered, however, the error is trapped in the form's Error( ) event code, which displays a more detailed message indicating what the problem is. Then, focus is set back to the Customer ID field, allowing the user to make a change to the primary key. ׽9h4wvc!tQ)d͌Many features of the sample application are achieved by writing method code. Most of the method code runs because of standard events in the Visual FoxPro event model, but some are custom-defined and invoked programmatically. When you want to see the code used to implement a feature, select the feature and then choose Code in the Behind the Scenes form. The actual method code is contained in the form definition file (.SCX) or class library (.VCX). Behind the Scenes simply opens the.SCX or.VCX as a table, locates the record specified in the Code_to_sh field of the Behindsc table, and presents the appropriate part of the memo. Behind the Scenes has also been designed to retrieve one or more stored procedures from the current database. E܋The Tag column in the grid on the Order History form allows the user to mark individual items and then copy those items to an order. The column is only meaningful when the Order History form is displayed with the Last Order command button in the Order Entry form. The check box itself was added to the grid visually by selecting a column and dropping a check box on it. For details, see Chapter 11 in the Visual FoxPro Developer's Guide. In order to retain the value of the check box for each row in the grid, it must be bound to a field in a table or view. Since the order_line_items table does not contain a column that allows this, an updatable view was created with the first field set to a literal value: SELECT .F., ..... This will result in a column filled with the value .F. when the view is created. It is this column that the check box on the grid is bound to. Since the new column does not have a corresponding column in the order_line_items table, the original table is not affected. fExEtu}֥M_^]Ë6}փX #This stored procedure determines a customer's remaining credit by querying the Orders and Order Line Items tables and returning the total order amount of all the orders for the current customer. All the order amounts are then summed, and this value is subtracted from the customer's maximum order amount (customer.max_order_amt) to derive the remaining credit, which is returned to the calling program. This stored procedure is called from the Available Credit command button of the Order Entry form.UVisual FoxPro's support for parameterized views and the ability to have code attached to a data environment in a report make it easy to accept user criteria for a report at run time: 1. Create a parameterized view with the View Designer and add the view to the report's data environment. Set the AutoOpenTables property of the data environment to .F.. 2. Create a modal form that allows the user to select report criteria, such as a specified group of employees. 3. In the Init event code of the data environment, store the values specified by the user to the variables referenced in the parameters of the view. Once the variables have been initialized, call the OpenTables( ) method of the report's data environment. Two reports in Tasmanian Traders accept user-specified criteria: LISTEMPL.FRX and ORDERS.FRX. The ListEmpl report allows a user to specify a job title so that just employees with that job title are printed. Because only a single value is required for the parameter, the code in the Init event of the data environment uses the TO keyword of the DO FORM commmand to store the desired job title directly to the parameter variable. The Orders report allows a user to specify a range of dates, so the code in the Init event of the data environment uses the LINKED keyword of the DO FORM command. The OK button on the GetInv form hides the form instead of releasing it. Code in the Init event of the data environment then stores values from the form into the appropriate view variables and then releases the form object. yj\W^kuwqlo{yzyk^X^juJTo make adding items to the grid as convenient as possible for keyboard users, a custom shortcut menu is displayed whenever the Order Entry is displayed, and is enabled whenever the Order Entry form has the focus. The menu is initially displayed from the Order Entry form's Load() event method with the following code: DO menus\ordentry.mpr The menu is released from the form's Unload() event method with the following code: RELEASE PAD orderentry OF _MSYSMENU The following code is used in the menu pad's SKIP FOR clause to control when the Entry menu pad is enabled: !WONTOP("frmorderentry") To control when the Add Items menu bar option is enabled, the following code is used in the SKIP FOR clause: !FormIsObject() OR !_SCREEN.Activeform.lAllowEdits And finally, to control when the Remove Items menu bar option is enabled, the following code is used in the SKIP FOR clause: !FormIsObject() OR !_SCREEN.Activeform.lAllowDelete OR ; TYPE("_SCREEN.Activeform.Activecontrol") <> "O" OR ; UPPER(_screen.Activeform.Activecontrol.BaseClass) <> "GRID" This code ensures that the Remove Items menu bar option will be disabled if the current form's lAllowDelete property is .F., or if the active control on the current form is not an object, or if the active control on the current form is not a grid. ~u~put zVt$WThe picture of the category can be changed by clicking on the Change Picture button. (The button's caption changes to say Add Picture if you are adding a new category.) Code associated with the Click event of this button allows you to select a BMP file using the GETFILE( ) function. This file is then appended into the picture field of the Category table using the APPEND GENERAL command.f䜤m8ް׆Pd #t;FӮl_nwGJQMThe introductory form is an instance of the IntroForm class in TSGEN.VCX. The IntroForm class is a subclass of tsFormRetVal in TSBASE.VCX, the parent class of all forms that return values. You can prevent the introduction form from being displayed each time you run Tasmanian Traders by clearing the Show This Form at Startup check box. The picture on the form was implemented by setting the Picture property of the form to the name of the bitmap file containing the Tasmanian Traders logo. The text was placed on the form using three label controls with the BackStyle property set to 0-Transparent. The three command buttons at the bottom of the form allow you to continue into the Tastrade application (Continue), exit back to the command window (Exit), or bring up this form (Behind the Scenes). The code in the Click() event method of the Continue and Exit command buttons sets the uRetValue property of the form and sets the Visible property of the form to .F.. The uRetValue property of the form holds an integer value that indicates to the calling program which button was pressed. The totals displayed near the bottom of the screen are calculated in the custom SumColumn( ) method of the tsGrid class in TSBASE.VCX. The grid's custom cFieldToSum property is set to quantity * unit_price. Each time the grid is refreshed, it calculates the value and stores the result in the grid's custom nColumnSum property. The code required to display the totals is minimal. First, in the grid's Refresh event code, the parent class Refresh event code is called to force the default behavior of tsGrid, then the value of the nColumnSum property is stored to the Value property of the Item Subtotal textbox, txtSubTotal. Next, because txtSubTotal changed, the ProgrammaticChange event is fired for that control. Code in the ProgrammaticChange event for the Item Subtotal text box (a member of the OrderEntry class in ORDER.VCX) calculates the discount amount (based on the discount percentage stored in the text box to the left of the percent sign), and stores the result in the Discount textbox. Finally, code in the ProgrammaticChange event for the Discount textbox calculates the grand total for the order by adding on the freight charge, which is read from the Freight textbox, and stores this total value in the Invoice Total text box. 7U]?7U]?7U]?7U The Order History form is the only form in Tasmanian Traders that allows multiple instances. To support multiple instances of this form: 1. The DataSession property of the form is set to 2-Private Data Session. This allows multiple copies of the underlying data to be opened at the same time. 2. A numeric suffix is attached to the form's name and caption, indicating an instance number for each form. The custom AddInstance( ) method, in the Application class in TSGEN.VCX, tracks the number of current instances and returns the next available instance number. AddInstance( ) is called in the form's Init event code, to get the instance number and attach it to the form's name and caption. Note that this is done before the call to the parent class Init since the parent class Init is responsible for adding the form's caption to the Window menu. Here is what the code looks like: THISFORM.cOriginalFormName = THISFORM.Name THISFORM.Name = THISFORM.Name + ALLTRIM(STR(oApp.AddInstance(THISFORM.Name))) THISFORM.Caption = THISFORM.Caption + ":" + RIGHT(ALLTRIM(THISFORM.Name), 1) When the form is closed, the application object needs to decrement the instance count. To achieve this, the following code is used in the form's Destroy event: oApp.RemoveInstance(THISFORM.cOriginalFormName) 3. The first time the Order History form is displayed, its instance number will be 1. The second instance will be 2, and so on. However, if there are three instances numbered 1, 2, and 3, and the user closes instance number 1, the next instance is still given the number 4, rather than the newly-available 1, for simplicity. 4. When each new instance of the Order History form is displayed, it is offset from the position of the previous form. This behavior requires the following maintenance information: a. A way of identifying the group of instances as a whole b. A place to store the most recent instance's position c. The number of currently running instances d. The next available instance number This information is maintained in a custom property array, aInstances[ ], of the application class. It is a two-dimensional array with four columns, satisfying the four storage requirements outlined above. Col1 holds the original form name. Col2 holds a reference to the most recently opened instance. Col3 holds the number of currently running instances. Col4 holds the next available instance number. The responsibility of the custom AddInstance( ) method of the Application class is to maintain this array. The form itself is passed as a parameter to this method. The array is scanned for the form's name to see if the form already exists. The array is then expanded if necessary. Next, the form's position is set based on the most recent instance. Finally, the number of current running instances and the next available instance number are incremented, and the instance number is returned to the calling program. The custom RemoveInstance( ) method of the Application class is responsible for decrementing the instance count for a particular instance, and removing that row from the array (or clearing the array if only one row exists) when the last instance is closed. 7U]?7U]?7U]?7U]?7U The Employee list is filled in the Init event code of the Login class in LOGIN.VCX. A SQL SELECT statement is built and assigned to the RowSource property of the combo box. The Requery( ) method of the combo box is then called to execute the query. LOCAL lcFieldName lcFieldName = THISFORM.cFieldName THISFORM.cboName.RowSource = "SELECT " + ; lcFieldName + ; " FROM " + THIS.cTable + ; " ORDER BY " + lcFieldName + ; " INTO CURSOR cNames" THISFORM.cboName.Requery( ) Each time an employee is selected from the drop-down list, the form's Refresh( ) event method is called. The code in this event does a search on the table specified in the cTable property of the form in order to reposition the record pointer so that the rest of the form's contents can be filled in. >O+QYT'QZ''V_7U M]?7U]?7U]?7UThe Employees form maintains all employee information. The parent class of this form is tsMaintForm in TSBASE.VCX. Most of this form's functionality is defined in the tsMaintForm class or in tsMaintForm's parent class, tsBaseForm. Because the fields in the Employee table wouldn't all fit on one page, an Additional Information page was added to the page frame. The page was added by changing the PageCount property of the PageFrame control to 3. When a user adds a new employee, the default password is initialized to "Tastrade". This value is specified to be the default value of the password field in the table definition. You must log in as the new employee to change the password. Note that when the application is running, if the current record is for the currently logged-in employee, code in the form's Refresh( ) event method sets the form's lAllowDelete property to .F. so that the Delete item on the File menu is disabled.?7U]?7 When the Behind the Scenes form opens, the value in the drop-down list display the form that was active when Behind the Scenes was launched, and the Design Feature list displays the items associated with this form. Before the form is displayed, the Load event code determines the context from the Caption property of _SCREEN.ActiveForm. In the Init event code, the form array property aForms[ ] is filled with a SELECT statement that returns the distinct values in the screen_id field of BEHINDSC.DBF. The All item is added with the AINS( ) function. The RowSource property of the drop-down list is then set to the form property aForms[ ]. A call to the ASCAN( ) function returns the element of the array that matches the context, and the Value property of the drop-down list is set to this value. If no match is found, the Value of the drop-down list is set to 1. In the code associated with the Init event of the Behind the Scenes form, a filter is set on BEHINDSC.DBF so that only the relevant topics are displayed in the Design Feature list. In the code associated with the InteractiveChange event for the drop-down list, the filter is reset based on the drop-down list's Value property. The Design Features list Requery( ) method is called to update the list's contents. x&?h"m:Jj 6 _:UQThe NewID( ) stored procedure creates unique IDs in the system. It returns the default value for the primary keys for the Supplier, Products, Employee, Category, Shippers, and Orders tables. Code in NewID( ) opens SETUP.DBF, looks for table alias in the Key_name field, reads the current value of the Value field, increments it by 1, then writes it back to SETUP.DBF. The value that was read from the Value field before incrementing is then returned as the primary key value for a record. Note that the NewID( ) stored procedure is also designed to accept an alias as a parameter. The same technique could then be used to maintain incrementing values that were not being used as primary keys. An example of this is the order_number record, which is used to generate order numbers for the Orders table. You can modify stored procedures by first opening the database with the OPEN DATABASE command, and then executing the MODIFY PROCEDURES command to bring up an editing window. Alternatively, you can use the MODIFY DATABASE command, then click on the Stored Procedures button on the toolbar. Z1&!ZbBe΁8!K9**SB5b.iThe Reports form, a generic form, allows users to select from a list of available reports and listings, and send the output to the destination of their choice. The reports and listings shown in the list box are stored in REPOLIST.DBF. Since this table is considered metadata, and does not pertain to the data maintained by Tasmanian Traders, it is not included in the TasTrade database. 4EIU[i]M щc !A%Hv | 6eJфg5h[A convenient technique for saving application-wide settings is through the use of an INI file. INI files are easy to create and maintain using functions in the Windows API. When a user changes the Show This Form At Startup check box setting, the value is written to the ShowIntroForm setting in the [Defaults] section of the TASTRADE.INI file. A value of 0 indicates that the introductory form will not be displayed in subsequent sessions. This value is checked in the code associated with the Init event of the TasTrade class in MAIN.VCX. DECLARE statements in MAIN.PRG allow the application to directly use Windows API functions as if they were a part of the Visual FoxPro language: DECLARE INTEGER GetPrivateProfileString IN Win32API AS GetPrivStr ; String cSection, String cKey, String cDefault, String @cBuffer, ; Integer nBufferSize, String cINIFile DECLARE INTEGER WritePrivateProfileString IN Win32API AS WritePrivStr ; String cSection, String cKey, String cValue, String cINIFile _+DateRange is a custom control based on the Visual FoxPro Control base class. It is meant to be used anywhere the user needs to enter a range of dates. The To: date is assumed to be greater than the From: date. Since the text box controls in the DateRange control are protected and cannot be accessed outside the DateRange control, two custom methods were created to return the value of these text box controls. GetDateFrom: Returns the value of the From: text box. GetDateTo: Returns the value of the To: text box. A third custom method, Validate( ), was created to verify the dates. If you subclass this control, you can override this method to implement your own validation behavior. Validate: Returns .F. if the To: date is less than the From: date, as long as the To: date is not empty. ՕXJUF^u The Order Entry form is an example of implementing one to many forms with Visual FoxPro. Much of the form's functionality is defined in the OrderEntry class in the ORDER.VCX class library. All of the text box controls on the Order Entry form are an instance of the OrdTextBox class in ORDER.VCX. This was done to facilitate custom refresh code, which enables or disables the control based on certain conditions. The customer combo box is an instance of the intelli-find combo box in tsBase: tsifCombo. It allows you to add new customers directly to the text portion of the combo. If a user enters a customer that is not in CUSTOMER.DBF, code in the combo prompts the user to add the new customer to the customer table. 2XX9=Q?)#wThe About Form class is a reusable class that displays information about the application, and makes calls to the Windows API to display information about the environment. The program first reads the default user name and corporation name from the "MS USER INFO" section of the WIN.INI file, located in the Windows directory. Next, a file name is read from the "MICROSOFT SYSTEM INFO" section of the WIN.INI file. This file name holds the name of the program to run when the user clicks the System Info button on the form. This same information is retrieved from the system registry database under Windows 95 and Windows NT. Detail lines are added to the grid by right-clicking anywhere in the grid. The shortcut menu that is displayed allows a user to choose between adding or deleting an item. A user can also press Ctrl+Insert to add an item or Ctrl+Delete to delete an item. These access keys are defined in the menu that appears whenever the Order Entry form is displayed. Note that if the order Due By date has passed, the user cannot add or delete line items. 7U]?7U]?7U]?7U]?7U]?7U]?7U]?7U]?7U #U]?7U]?7U M]?7U]tThe Change Password form is a modal form based on the tsBaseForm in TSBASE.VCX. It is used to change passwords for employees in the Employee table. The Hint text box displays the current employee's password so you don't have to remember it. As you type in the old password, it is validated character-by-character in code associated with the InteractiveChange event against the THISFORM.cOldPassword property. This property is initialized with the currently logged-in employee's password in the form's Load event code. As soon as the old password is correct, the New Password and Confirm New Password text boxes are enabled. D_Since the Change Password form is a modal form, the Behind the Scenes form must be displayed modally as well so that it doesn't get forced behind Change Password. This is accomplished by having the Behind the Scenes form's Init( ) code accept a parameter that indicates if it should display itself modally or not: LPARAMETERS tlModal THISFORM.WindowType = IIF(tlModal, 1, 0) N)9猀T$+,$? Qu?Kd2MV?F"Y^VVCThe form's Load( ) event code looks up the current employee ID in the Employee table, which is stored as a property of the global Application object (oApp.cEmployeeID). The employee's old password is stored in the cOldPassword property of the form. The form's Activate event code disables the "Behind the Scenes" command button if the "Behind the Scenes" window already exists: THISFORM.cmdBehindSC.Enabled = !WEXIST("frmBehindSC") The Employee table is then selected. This form uses a private data session to avoid potential conflicts with other tables. This was accomplished by setting the DataSession property of the form to 2-Private Data Session at design time. KY ?ӰuThe Last Order button is designed for a situation in which customers want to order the same items that they have previously ordered. Clicking this button will bring up an instance of the "Order History" form with the customer's last order displayed, allowing the user to tag selected items from the last order and add them to the current order. Additionally, a filter is set within the "Order History" form that allows the user to scroll through all orders for the current customer. When the "Order History" form is called, it is passed a reference to the current "Order Entry" form so that the "Order History" form can manipulate the "Order Entry" form. See the "Order History" form entriesThe RemainingCredit( ) stored procedure in TASTRADE.DBC performs a query against the Orders and Order Line Items tables, which returns the total order amount of all the orders for the current customer. All the order amounts are then summed, and this value is subtracted from the customer's maximum order amount (customer.max_order_amt) to derive the remaining credit, which is returned to the calling program. ing credit, which is returnedThe controls on the Order Entry form are disabled if today's date is greater than the Due By date. This is accomplished in the Refresh( ) method of the OrdTextBox class in ORDERS.VCX. Each time the Order Entry form is refreshed, all the controls on that form are refreshed as well. The Enabled property of each control is set based on the lAllowEdits property of the form. The lAllowEdits property is set in the Refresh( ) method of the Due By text box of the OrderEntry class. =<:>p\4,cThis form manages adding new customers from the Order Entry form. It contains an instance of the container class CustomerInfo in TSGEN.VCX. This container contains all the controls needed to enter new customer information, and is the same container that is used in the customer maintenance form. Code in the form's Init( ) event appends a blank record to the Customer table. The code associated with the Click event of the OK button saves the information to the table, while the code associated with the Click event of the Cancel button issues TABLEREVERT( ) to revert the table back to its original state. mUCi5TZ}&xpThe Run command button's (cmdRun) Click event code first verifies that the selected FRX file actually exists. Then, using a CASE structure, it decides where to send the output based on the user's selection in the form. The report is previewed with the PREVIEW keyword of the REPORT FORM command: REPORT FORM (lcSeleRepo) PREVIEW To print the report, use the PRINTSTATUS( ) method to determine if the printer is on-line. If this function returns .T., add the TO PRINTER NOCONSOLE clause to the REPORT FORM command. IF PRINTSTATUS( ) REPORT FORM (lcSeleRepo) ; PRINTER NOCONSOLE ELSE =MESSAGEBOX("Printer not ready", MB_ICONEXCLAMATION) ENDIF To send the report to a file, use the ASCII clause. Optional code can prompt the user for a file name using the GETFILE( ) or PUTFILE( ) function, in place of using a default ".TXT" extension: lcTextFile = ALLTRIM(Repolist.cDosName)+".TXT" REPORT FORM (lcSeleRepo) TO FILE (lcTextFile) ASCII =MESSAGEBOX("File Saved As " + FULLPATH(lcTextFile), ; MB_ICONINFORMATION, ; "Tasmanian Traders") ML=DAny form based on tsBaseForm has a cToolBar property that stores the name of the toolbar to display when the form is displayed. The form's Init event code passes the value of cToolBar to the application object method ShowNavToolBar( ). The ShowNavToolBar( ) method maintains the existing forms that need the toolbar, as well as creating and displaying the toolbar itself. The toolbar is displayed before the form is displayed because the toolbar comes up in a docked position for the first time, which changes the client area of the main Visual FoxPro window. This means that if a form were already displayed when the toolbar displays, the form would be repositioned. You can still see this behavior in Tasmanian Traders by displaying a form, and then docking and undocking the toolbar. dEj?N]U8{NCThe Top and Left properties of the form are saved to the application's INI file by calling the SaveWindowPos( ) method of the form, which calls the WritePrivateProfileString( ) Windows API. This function is declared in MAIN.PRG, and given the alias WritePrivStr. The following is a list of the parameters expected by this function and the values passed from SaveWindowPos( ): 1. The name of the section to search in: "WindowPositions" 2. The entry identifier to store: THISFORM.Caption 3. The value of the entry to store: ALLTRIM(STR(THISFORM.Top)) + "," + ALLTRIM(STR(THISFORM.Left)) 4. The name of the INI file: CURDIR() + INIFILE Note: INIFILE is a #DEFINE constant declared in TASTRADE.H If the INI file does not exist, it is created. If the entry in the specified section does not exist, it is created. `i@{;y+{-Z<5_ǝHK&͹PQx6The Top and Left properties of the form are saved to the application's INI file each time the form is closed. They can then be restored when the form is run. This is accomplished by calling the form's RestoreWindowPos( ) method from the code associated with the form's Init event. The RestoreWindowPos( ) method uses the GetPrivateProfileString( ) Windows API to read the Top and Left properties from the application's INI file. The GetPrivateProfileString( ) function is declared in MAIN.PRG and given the alias GetPrivStr. The following is a list of the parameters expected by this function and the values passed from RestoreWindowPos( ): 1. The name of the section to search in: "WindowPositions" 2. The entry to search for: THISFORM.Caption 3. The value to return if the entry is not found. "" 4. A place to hold the results of the search @lcBuffer 5. How many bytes to read in: LEN(lcBuffer) 6. The name of the INI file: CURDIR() + INIFILE Note: INIFILE is a #DEFINE constant declared in TASTRADE.H The function returns the number of bytes read, and stores the value of the entry to the lcBuffer variable. This variable is then parsed to retrieve the form's Top and Left property settings. If no error occurs while lcBuffer is being parsed, the values are assigned to the form's properties. oBy default, Visual FoxPro displays the Name of existing forms on the Window menu. A friendlier method, especially when there might be multiple instances of a form, is to display the caption of the existing form on the Window menu. Two custom methods in tsBaseForm accomplish this: AddToMenu( ) and RemoveFromMenu( ). Code in the AddToMenu( ) method, called in the Init event code of the form, first finds the next available bar number, then defines a new bar with the DEFINE BAR command, using the Caption property of the form as the prompt. An ON SELECTION BAR command is then issued to activate the form whenever that bar is chosen. When the form is destroyed, the form's caption must be removed from the Window menu. This is accomplished by looping through the currently defined bars of the Window menu, searching for the current form's caption, and issuing the RELEASE BAR command.orm is not an object. This is accomplished by a function CWhenever the user attempts to do something that will either cause a form to be closed or a different record displayed, the code needs to check for changes. Code in the DataChanged( ) method calls the GETFLDSTATE( ) function, passing it a -1 so that it returns a string representing the current state of all the fields in the current record. If this string contains the values '2' or '4', the data in the row has changed, and the DataChanged( ) method returns .T. Creating a custom method instead of directly calling GETFLDSTATE( ) overrides checking for changed data in particular forms. For example, a one-to-many form needs to check if any changes were made to the "many" side. (See the Order Entry form for more details). Another example would be returning .T. if a value had changed in an unbound control on a specific form.er() method to ensure the value of the current controOccasionally, a user attempts to close a form before explicitly saving changes. The form's QueryUnload event code calls the DataChanged( ) method to see if any data has been changed. If so, the AskToSave( ) method is called. If AskToSave( ) returns IDCANCEL (a #DEFINE constant in FOXPRO.H), NODEFAULT is issued, which prevents the form from being closed. This will work even if the user is trying to quit the application. Because the QueryUnload event is not triggered when a form is closed by the RELEASE command or the Release( ) method, code associated with the Close command button on the toolbar explicitly calls the QueryUnload event code of the currently active form before releasing the form.view rather than directly from the order_line_items tablIf the user is attempting to close the form or move to a different record and changes have been made but not saved, the user is prompted to save, discard, or cancel the changes. This is accomplished in the form's custom AskToSave( ) method. This method displays a message box with the MESSAGEBOX() function, and allows the user to choose from three options. 1. Save the changes: The form's custom Save() method is called. 2. Don't save the changes: The form's custom Restore() method is called. 3. Cancel: The user is returned to the editing session. The AskToSave( ) method returns the value of whatever the MESSAGEBOX() function returns (IDYES, IDNO, or IDCANCEL). History form wasWith bound controls and buffered records, control values are not written to the record buffer until the control loses focus. If a form has only a single control or the user changes a control then immediately chooses a toolbar button, this delayed write is a problem. The custom WriteBuffer() method handles the problem of flushing the contents of the current control by: 1. Ensuring that the active control is an object, and if so, not a grid. (Grids are usually table-buffered, and must be handled separately) 2. Ensuring the ControlSource property of the active control is not empty. If it is empty, it means that the control is not bound to a field in the table. 3. Comparing the contents of the field (i.e., the value in the buffer) to the value of the control. If the values aren't equal, the data in that control has changed. 4. If data has changed, issuing a REPLACE statement that updates the contents of the field specified in the ControlSource property with the contents of the current control.itWhen executing code that might be time consuming, well-designed applications cue users that they must wait for processing to finish. A good way to do this is to change the mouse cursor to an hourglass. This is accomplished in the custom WaitMode() method of this form class. Passing the value .T. to this method sets the MousePointer property for all controls on the form to the MOUSE_HOURGLASS value. If the parameter passed is .F., then the MousePointer property for all controls is set to the MOUSE_DEFAULT value. (These values are #DEFINE constants defined in FOXPRO.H). Setting the MousePointer value for all controls on a form ensures that the proper mouse cursor is displayed no matter where the user moves the mouse. is enabled whenever the Order E